import {useUserInfoStore} from "@/stores/user_info";
import {useFileInfoStore} from "@/stores/file_info";
import {fileChunk, mergeFile, fileCheck, fileUpload} from "@/api/file";
import {ElNotification} from 'element-plus'


// 文件上传
export const oneFileUpload = () => {
    // 引入文件信息存储模块
    const fileInfoStore = useFileInfoStore();
    // 初始化当前文件索引
    let currentIndex = fileInfoStore.currentFileIndex;
    // 获取当前选中的文件项
    let fileItem = fileInfoStore.files[currentIndex]
    // 获取当前选中的文件
    const file = fileItem.file
    // 初始化进度值
    let progress = 0;
    // 初始化父ID
    let parentId = fileItem.parentId;
    // 初始化文件名
    let filename = fileItem.name;
    return new Promise((resolve, reject) => {
        const worker = new Worker(new URL('fileWorker.js', import.meta.url), {
            type: 'module'
        });
        worker.postMessage({
            file
        })
        worker.onmessage = async (res) => {
            const chunkSize = 1024 * 1024; // 1MB
            const size = file.size;
            const totalNum = Math.ceil(size / chunkSize);
            // 修改文件上传进度
            progress += Math.ceil(100 / totalNum);
            fileInfoStore.files[currentIndex].analyzeSchedule = progress < 100 ? progress : 100;
            if (res.data.md5) {
                // 解析完成文件状态设为上传中
                fileInfoStore.files[currentIndex].status = 2
                // 开始上传文件
                let formData = new FormData();
                formData.append("file", file);
                formData.append("fileMd5", res.data.md5);
                formData.append("parentId", parentId);
                formData.append("fileName", filename);
                formData.append("fileSize", file.size);

                fileCheck({
                    "md5": res.data.md5,
                    "fileName": filename,
                    "parentId": parentId,
                    "fileSize": file.size
                }).then(async (res: any) => {
                    if (res.exist) {

                        // 文件已存在，修改文件状态为成功
                        fileInfoStore.files[currentIndex].status = 3
                        ElNotification({
                            message: filename + "上传成功",
                            duration: 1500,
                            type: 'success',
                        })
                        resolve(true)

                    } else {
                        await fileUpload(formData).then((res: any) => {
                            // 文件上传完成，修改文件状态为成功
                            if (res.code == 200) {
                                fileInfoStore.files[currentIndex].status = 3
                                ElNotification({
                                    message: filename + "上传成功",
                                    duration: 1500,
                                    type: 'success',
                                })
                            }
                            resolve(true)

                        }).catch((err) => {
                            // 文件上传失败，修改文件状态为失败
                            fileInfoStore.files[currentIndex].status = 4
                            ElNotification({
                                message: filename + "上传失败",
                                duration: 1500,
                                type: 'error',
                            })
                            resolve(true)

                        })
                    }


                })


            }

        }

    })
}

// 开启子线程进行分片并计算MD5值后分片上传文件
export const chunkUploadFile = (chunkSize) => {
    // 引入文件信息存储模块
    const fileInfoStore = useFileInfoStore();
    // 初始化当前文件索引
    let currentIndex = fileInfoStore.currentFileIndex;
    // 获取当前选中的文件项
    let fileItem = fileInfoStore.files[currentIndex]
    // 获取当前选中的文件
    const file = fileItem.file
    // 初始化进度值
    let progress = 0;
    // 初始化父ID
    let parentId = fileItem.parentId;
    // 初始化文件名
    let filename = fileItem.name;
    return new Promise((resolve, reject) => {
        // 总分片数量
        let chunkCount = Math.ceil(file.size / chunkSize);
        // 分片信息集合
        let chunks: any = [];
        // 分片MD5值集合
        let results: any = [];
        // 当前电脑的线程数量
        let thread = navigator.hardwareConcurrency;
        // 每个线程处理的分片数量
        let threadCount = Math.ceil(chunkCount / thread);
        let finish = 0;
        for (let i = 0; i < thread; i++) {
            // 开启子线程进行分片并计算MD5值
            const worker = new Worker(new URL('./chunkFileWorker.js', import.meta.url), {
                type: 'module'
            });
            // 每个线程处理的分片数量
            let start = 0;
            let end = 0;
            start = i * threadCount
            end = start + threadCount
            if (start > chunkCount) {
                continue
            }
            if (end > chunkCount) {
                end = chunkCount;
            }
            // 向子线程中传递分片信息
            worker.postMessage({
                chunkCount,
                file,
                chunkSize,
                start,
                end
            })
            // 监听子线程返回的分片信息
            worker.onmessage = (e) => {
                // 更新解析进度
                progress += Math.ceil(100 / chunkCount);
                fileInfoStore.files[currentIndex].analyzeSchedule = progress < 100 ? progress : 100;
                // 存储分片信息
                chunks[e.data.index] = e.data;
                // 存储分片MD5值
                results[e.data.index] = e.data.has;
                finish++;
                if (finish === chunkCount) {
                    worker.terminate()
                    // 计算文件分片MD5值的MD5值
                    const fileWorker = new Worker(new URL('./md5worker.js', import.meta.url), {
                        type: 'module'
                    });
                    fileWorker.postMessage({
                        results
                    })
                    fileWorker.onmessage = async (res: any) => {
                        // 分片MD5值的MD5值
                        const fileMd5 = res.data;
                        // 查看文件是否已上传过
                        fileCheck({
                            "md5": fileMd5,
                            "fileName": filename,
                            "parentId": parentId,
                            "totalNum": chunkCount,
                            "fileSize": file.size
                        }).then(async (val: any) => {
                            // 解析完成文件状态设为上传中
                            fileInfoStore.files[currentIndex].status = 2
                            if (val.exist) {
                                // 文件已存在，修改文件状态为成功
                                fileInfoStore.files[currentIndex].status = 3
                                ElNotification({
                                    message: filename + "上传成功",
                                    duration: 1500,
                                    type: 'success',
                                })
                                resolve(true)
                            } else {
                                // 文件不存在，开始分片上传
                                let num = -1;
                                if (val.index) {
                                    // 先查看文件是否已上传过，上传过则从后端返回的分片个数开始上传
                                    num = val.index
                                }
                                // 开始文件上传
                                await uploadFile(file, chunks, fileMd5, 5, parentId, chunkCount, filename, currentIndex, num).then(res => {
                                    // 无论文件上传成功或失败，都返回成功调下一个文件上传
                                    fileInfoStore.files[currentIndex].status = 3
                                    ElNotification({
                                        message: filename + "上传成功",
                                        duration: 1500,
                                        type: 'success',
                                    })

                                    resolve(true)
                                    const userInfo = useUserInfoStore()
                                    userInfo.selectUserInfo()
                                    if (fileInfoStore.nextFileIndex != -1) {
                                        fileInfoStore.files[fileInfoStore.nextFileIndex].status = 1;
                                    }

                                }).catch(err => {
                                    fileInfoStore.files[currentIndex].status = 4
                                    ElNotification({
                                        message: filename + "上传失败",
                                        duration: 1500,
                                        type: 'error',
                                    })

                                    resolve(true)
                                    const userInfo = useUserInfoStore()
                                    userInfo.selectUserInfo()
                                    if (fileInfoStore.nextFileIndex != -1) {
                                        fileInfoStore.files[fileInfoStore.nextFileIndex].status = 1;
                                    }

                                })
                            }
                        })

                    }
                }
            }

        }
    })
}

const uploadFile = async (file, results, md5, num, parentId, totalNum, filename, currentFileIndex, chunkIndex) => {
    if (num > totalNum) {
        num = totalNum
    }
    let currentIndex = 0;
    let finish = 0;
    if (chunkIndex != -1) {
        finish = chunkIndex
        currentIndex = chunkIndex
    }
    const fileInfoStore = useFileInfoStore();
    const userInfoStore = useUserInfoStore();
    let progress = 0;
    // 记录上传是否出现错误
    let isError = false;
    return new Promise((resolve, reject) => {
        const upload = async () => {
            // 记录当前分片索引
            let index = currentIndex;
            currentIndex++
            // 创建表单上传数据
            const item = results[index]
            let formData = new FormData();
            formData.append("file", item.blob);
            formData.append("fileMd5", md5);
            formData.append("chunkFileMd5", item.has);
            formData.append("parentId", parentId);
            formData.append("fileName", filename);
            formData.append("fileSize", item.size);
            formData.append("chunkNumber", index + "");
            formData.append("totalNumber", totalNum + "");
            // 文件分片上传
            fileChunk(formData).then(async (res: any) => {
                if (res.code == 200) {
                    // 修改文件上传进度
                    progress += Math.ceil(100 / totalNum);
                    fileInfoStore.files[currentFileIndex].fileUploadSchedule = progress < 100 ? progress : 100;
                    // 还有分片或没有出现上传错误时继续上传
                    if (currentIndex < totalNum && !isError) {
                        // 递归上传
                        upload()
                    }
                    finish++;
                    if (finish == totalNum) {
                        // 文件分片上传完成，合并文件
                        await mergeFile({
                            "fileName": filename,
                            "fileMd5": md5,
                            "totalChunk": totalNum,
                            "size": file.size,
                            "parentId": parentId
                        }).then((res) => {
                            // 文件上传完成，修改文件状态为成功
                            resolve(true)
                            userInfoStore.selectUserInfo()
                        }).catch((err) => {
                            // 文件上传终止，修改文件状态为失败
                            isError = true
                            reject()
                            userInfoStore.selectUserInfo()
                        })
                    }
                }
            }).catch((err) => {
                // 文件上传终止，修改文件状态为失败
                isError = true
                reject()
            })
        }

        for (let i = 0; i < num; i++) {
            upload()
        }
    })
}
